﻿using HelixToolkit.Wpf.SharpDX;
using HelixToolkit.Wpf.SharpDX.Assimp;
using HelixToolkit.Wpf.SharpDX.Model.Scene;
using Microsoft.Win32;
using SharpDX;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using X.Toolkits.Drawing.Extends;
using X.WPF.Controls.Constants;
using X.WPF.Controls.CustomControls;
using X.WPF.Controls.Enums;
using X.WPF.Controls.EventArgs.Marker3DCanvasArgs;
using X.WPF.Controls.EventArgs.ThreeDArgs;
using X.WPF.Controls.Extends;
using X.WPF.Controls.Models;
using X.WPF.Controls.Services;
using X.WPF.Controls.Visual3Ds;
using Color = System.Windows.Media.Color;
using Geometry3D = HelixToolkit.Wpf.SharpDX.Geometry3D;
using MeshGeometry3D = HelixToolkit.Wpf.SharpDX.MeshGeometry3D;
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
using Point = System.Windows.Point;
using Size = System.Windows.Size;

namespace X.WPF.Controls.UserControls
{
    /// <summary>
    /// 3D标记画布组件
    /// </summary>
    public partial class Marker3DCanvas : UserControl
    {

        #region # 字段及构造器

        /// <summary>
        /// 选中的3D元素
        /// </summary>
        private Element3D _selectedElement3D;

        /// <summary>
        /// 临时标记点
        /// </summary>
        private PointsVisual3D _tempPointsVisual3D;

        /// <summary>
        /// 临时标记点
        /// </summary>
        private MeshGeometryModel3D _tempFeatureVisual3D;

        /// <summary>
        /// 是否正在编辑
        /// </summary>
        private bool _edit;

        /// <summary>
        /// 3D数模根节点
        /// </summary>
        private SceneNode _sceneRoot;

        /// <summary>
        /// 默认相机
        /// </summary>
        private readonly ThreeDCamera _defaultCamera;

        /// <summary>
        /// 第一个3D元素索引
        /// </summary>
        private readonly int _firstElement3DIndex;

        /// <summary>
        /// 绘制类型
        /// </summary>
        private ThreeDDrawType _drawType;

        /// <summary>
        /// 选中的绘制按钮
        /// </summary>
        private ThreeDDrawButtonInfo _checkedDrawButton;

        /// <summary>
        /// 绘制按钮列表
        /// </summary>
        private List<ToggleButton> _drawButtons;

        /// <summary>
        /// 首次渲染状态
        /// </summary>
        private bool _isFirstRender;

        /// <summary>
        /// 静态构造器
        /// </summary>
        static Marker3DCanvas()
        {
            // 注册依赖属性
            Marker3DCanvas.MouseLocationProperty = DependencyProperty.Register(nameof(Marker3DCanvas.MouseLocation), typeof(string), typeof(Marker3DCanvas), new PropertyMetadata(""));
            Marker3DCanvas.FilePathProperty = DependencyProperty.Register(nameof(Marker3DCanvas.FilePath), typeof(string), typeof(Marker3DCanvas), new PropertyMetadata(string.Empty, Marker3DCanvas.FilePathChanged));
            Marker3DCanvas.ColorProperty = DependencyProperty.Register(nameof(Marker3DCanvas.Color), typeof(Color), typeof(Marker3DCanvas), new PropertyMetadata(Colors.DarkGray, Marker3DCanvas.FilePathChanged));
            Marker3DCanvas.UseModeProperty = DependencyProperty.Register(nameof(Marker3DCanvas.UseMode), typeof(CanvasUseMode), typeof(Marker3DCanvas), new PropertyMetadata(CanvasUseMode.Nomal, Marker3DCanvas.UseModelChanged));
            Marker3DCanvas.StrokeThicknessProperty = DependencyProperty.Register(nameof(Marker3DCanvas.StrokeThickness), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d, StrokeThicknessChangedCallback));
            Marker3DCanvas.DrawButtonsProperty = DependencyProperty.Register(nameof(Marker3DCanvas.DrawButtons), typeof(ObservableCollection<ThreeDDrawButtonInfo>), typeof(Marker3DCanvas), new PropertyMetadata(new ObservableCollection<ThreeDDrawButtonInfo>(), DrawButtonsChangedCallback));
            Marker3DCanvas.MarkersProperty = DependencyProperty.Register(nameof(Marker3DCanvas.Markers), typeof(ObservableCollection<Marker3DMarker>), typeof(Marker3DCanvas), new PropertyMetadata(new ObservableCollection<Marker3DMarker>(), Marker3DCanvas.MarkersChanged));
            Marker3DCanvas.OperationModeProperty = DependencyProperty.Register(nameof(Marker3DCanvas.OperationMode), typeof(CanvasOperatorMode), typeof(Marker3DCanvas), new PropertyMetadata(CanvasOperatorMode.Nomal, Marker3DCanvas.OperatorModelChanged));
            Marker3DCanvas.EffectsManagerProperty = DependencyProperty.Register(nameof(Marker3DCanvas.EffectsManager), typeof(IEffectsManager), typeof(Marker3DCanvas), new PropertyMetadata(new DefaultEffectsManager()));
            Marker3DCanvas.IsRotationEnabledProperty = DependencyProperty.Register(nameof(Marker3DCanvas.IsRotationEnabled), typeof(bool), typeof(Marker3DCanvas), new PropertyMetadata(true));
            Marker3DCanvas.ShowViewCubeProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ShowViewCube), typeof(bool), typeof(Marker3DCanvas), new PropertyMetadata(true));
            Marker3DCanvas.ShowCoordinateSystemProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ShowCoordinateSystem), typeof(bool), typeof(Marker3DCanvas), new PropertyMetadata(true));
            Marker3DCanvas.ShowCameraInfoProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ShowCameraInfo), typeof(bool), typeof(Marker3DCanvas), new PropertyMetadata(true));
            Marker3DCanvas.ModelUpDirectionProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ModelUpDirection), typeof(ThreeDUpDirection), typeof(Marker3DCanvas), new PropertyMetadata(ThreeDUpDirection.Y, Marker3DCanvas.ModelUpDirectionChanged));
            Marker3DCanvas.CameraProperty = DependencyProperty.Register(nameof(Marker3DCanvas.Camera), typeof(ThreeDCamera), typeof(Marker3DCanvas), new PropertyMetadata(ThreeDConstants.UpDirectionZDefaultCamera, Marker3DCanvas.CameraChanged));
            Marker3DCanvas.Model3DProperty = DependencyProperty.Register(nameof(Marker3DCanvas.Model3D), typeof(SceneNodeGroupModel3D), typeof(Marker3DCanvas), new PropertyMetadata(null));
            Marker3DCanvas.SizeXProperty = DependencyProperty.Register(nameof(Marker3DCanvas.SizeX), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d));
            Marker3DCanvas.SizeYProperty = DependencyProperty.Register(nameof(Marker3DCanvas.SizeY), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d));
            Marker3DCanvas.SizeZProperty = DependencyProperty.Register(nameof(Marker3DCanvas.SizeZ), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d));
            Marker3DCanvas.ScaleXProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ScaleX), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d, Marker3DCanvas.ScaleXChanged));
            Marker3DCanvas.ScaleYProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ScaleY), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d, Marker3DCanvas.ScaleYChanged));
            Marker3DCanvas.ScaleZProperty = DependencyProperty.Register(nameof(Marker3DCanvas.ScaleZ), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(1d, Marker3DCanvas.ScaleZChanged));
            Marker3DCanvas.OffsetXProperty = DependencyProperty.Register(nameof(Marker3DCanvas.OffsetX), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(0d));
            Marker3DCanvas.OffsetYProperty = DependencyProperty.Register(nameof(Marker3DCanvas.OffsetY), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(0d));
            Marker3DCanvas.OffsetZProperty = DependencyProperty.Register(nameof(Marker3DCanvas.OffsetZ), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(0d));
            Marker3DCanvas.RotateAxisProperty = DependencyProperty.Register(nameof(Marker3DCanvas.RotateAxis), typeof(Vector3D), typeof(Marker3DCanvas), new PropertyMetadata(new Vector3D()));
            Marker3DCanvas.RotateAngleProperty = DependencyProperty.Register(nameof(Marker3DCanvas.RotateAngle), typeof(double), typeof(Marker3DCanvas), new PropertyMetadata(0d));
            Marker3DCanvas.FixedSizeProperty = DependencyProperty.Register(nameof(Marker3DCanvas.FixedSize), typeof(bool), typeof(Marker3DCanvas), new PropertyMetadata(true));


            // 注册路由事件
            Marker3DCanvas._Load3dModelBeforEvent = EventManager.RegisterRoutedEvent(nameof(Marker3DCanvas._Load3dModelBeforEvent), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Marker3DCanvas));
            Marker3DCanvas._Load3dModelAfterEvent = EventManager.RegisterRoutedEvent(nameof(Marker3DCanvas._Load3dModelAfterEvent), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Marker3DCanvas));
            Marker3DCanvas._MarkerDrawnEvent = EventManager.RegisterRoutedEvent(nameof(Marker3DCanvas.MarkDrawn), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Marker3DCanvas));
            Marker3DCanvas._MarkClickEvent = EventManager.RegisterRoutedEvent(nameof(Marker3DCanvas._MarkClickEvent), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Marker3DCanvas));
            Marker3DCanvas._MarkDragCompletedEvent = EventManager.RegisterRoutedEvent(nameof(Marker3DCanvas._MarkDragCompletedEvent), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Marker3DCanvas));

        }


        /// <summary>
        /// 构造器
        /// </summary>
        public Marker3DCanvas()
        {
            this.InitializeComponent();

            // 默认值
            this._edit = false;
            this._firstElement3DIndex = 5;
            this._defaultCamera = new ThreeDCamera();
            this._drawType = ThreeDDrawType.Center;
            this._drawButtons = new List<ToggleButton>();

            this.UseMode = CanvasUseMode.Nomal;
            this.OperationMode = CanvasOperatorMode.Nomal;

            // 重置工具栏操作模式组件
            this.ResetToolbarOperatorModeControl();

            // 重新加载操作模式
            this.ReloadOperatorMode();

            // 重新加载使用模式
            this.ReloadUseMode(this.UseMode);

            // 加载默认相机
            this.ReloadViewportModelUpDirection(this.ModelUpDirection);
            this.LoadDefaultViewportCamera();
            this.ReloadDefaultCamera(this.Camera);
        }
        #endregion

        #region # 依赖属性

        // 基础相关

        #region 鼠标位置 —— string MouseLocation
        /// <summary>
        /// 鼠标位置依赖属性
        /// </summary>
        public static DependencyProperty MouseLocationProperty;

        /// <summary>
        /// 鼠标位置
        /// </summary>
        public string MouseLocation
        {
            get => (string)base.GetValue(Marker3DCanvas.MouseLocationProperty);
            set => base.SetValue(Marker3DCanvas.MouseLocationProperty, value);
        }
        #endregion

        #region 文件路径 —— string FilePath
        /// <summary>
        /// 文件路径依赖属性
        /// </summary>
        public static DependencyProperty FilePathProperty;

        /// <summary>
        /// 文件路径
        /// </summary>
        public string FilePath
        {
            get => (string)base.GetValue(Marker3DCanvas.FilePathProperty);
            set => base.SetValue(Marker3DCanvas.FilePathProperty, value);
        }
        #endregion

        #region 使用模式 —— CanvasUseMode UseModel
        /// <summary>
        /// 使用模式依赖属性
        /// </summary>
        public static DependencyProperty UseModeProperty;

        /// <summary>
        /// 使用模式
        /// </summary>
        public CanvasUseMode UseMode
        {
            get => (CanvasUseMode)base.GetValue(Marker3DCanvas.UseModeProperty);
            set => base.SetValue(Marker3DCanvas.UseModeProperty, value);
        }
        #endregion

        #region 操作模式 —— CanvasOperatorMode OperationMode
        /// <summary>
        /// 操作模式依赖属性
        /// </summary>
        public static DependencyProperty OperationModeProperty;

        /// <summary>
        /// 操作模式
        /// </summary>
        public CanvasOperatorMode OperationMode
        {
            get => (CanvasOperatorMode)base.GetValue(Marker3DCanvas.OperationModeProperty);
            set => base.SetValue(Marker3DCanvas.OperationModeProperty, value);
        }
        #endregion 

        #region 颜色 —— Color Color
        /// <summary>
        /// 颜色
        /// </summary>
        public static DependencyProperty ColorProperty;

        /// <summary>
        /// 颜色
        /// </summary>
        public Color Color
        {
            get => (Color)base.GetValue(Marker3DCanvas.ColorProperty);
            set => base.SetValue(Marker3DCanvas.ColorProperty, value);
        }
        #endregion

        #region 画笔粗细 —— Brush StrokeThickness

        /// <summary>
        /// 画笔粗细依赖属性
        /// </summary>
        public static DependencyProperty StrokeThicknessProperty;

        /// <summary>
        /// 画笔粗细
        /// </summary>
        public double StrokeThickness
        {
            get => (double)base.GetValue(CanvasShape.StrokeThicknessProperty);
            set => base.SetValue(CanvasShape.StrokeThicknessProperty, value);
        }

        #endregion

        #region 绘制按钮列表 —— ObservableCollection<ThreeDDrawButtonInfo> DrawButtons
        /// <summary>
        /// 绘制按钮列表依赖属性
        /// </summary>
        public static DependencyProperty DrawButtonsProperty;

        /// <summary>
        /// 绘制按钮列表
        /// </summary>
        public ObservableCollection<ThreeDDrawButtonInfo> DrawButtons
        {
            get => (ObservableCollection<ThreeDDrawButtonInfo>)base.GetValue(Marker3DCanvas.DrawButtonsProperty);
            set => base.SetValue(Marker3DCanvas.DrawButtonsProperty, value);
        }
        #endregion

        #region 标记列表 —— ObservableCollection<Marker3DMarker> Markers
        /// <summary>
        /// 标记列表依赖属性
        /// </summary>
        public static DependencyProperty MarkersProperty;

        /// <summary>
        /// 标记列表
        /// </summary>
        public ObservableCollection<Marker3DMarker> Markers
        {
            get => (ObservableCollection<Marker3DMarker>)base.GetValue(Marker3DCanvas.MarkersProperty);
            set => base.SetValue(Marker3DCanvas.MarkersProperty, value);
        }
        #endregion


        // 3D 视口相关

        #region 效果器 —— IEffectsManager EffectsManager
        /// <summary>
        /// 效果器依赖属性
        /// </summary>
        public static DependencyProperty EffectsManagerProperty;

        /// <summary>
        /// 效果器
        /// </summary>
        public IEffectsManager EffectsManager
        {
            get => (IEffectsManager)base.GetValue(Marker3DCanvas.EffectsManagerProperty);
            set => base.SetValue(Marker3DCanvas.EffectsManagerProperty, value);
        }
        #endregion

        #region 是否可旋转 —— bool IsRotationEnabled
        /// <summary>
        /// 是否可旋转依赖属性
        /// </summary>
        public static DependencyProperty IsRotationEnabledProperty;

        /// <summary>
        /// 是否可旋转
        /// </summary>
        public bool IsRotationEnabled
        {
            get => (bool)base.GetValue(Marker3DCanvas.IsRotationEnabledProperty);
            set => base.SetValue(Marker3DCanvas.IsRotationEnabledProperty, value);
        }
        #endregion

        #region 显示立方体视图 —— bool ShowViewCube
        /// <summary>
        /// 显示立方体视图依赖属性
        /// </summary>
        public static DependencyProperty ShowViewCubeProperty;

        /// <summary>
        /// 显示立方体视图
        /// </summary>
        public bool ShowViewCube
        {
            get => (bool)base.GetValue(Marker3DCanvas.ShowViewCubeProperty);
            set => base.SetValue(Marker3DCanvas.ShowViewCubeProperty, value);
        }
        #endregion

        #region 显示坐标系 —— bool ShowCoordinateSystem
        /// <summary>
        /// 显示坐标系依赖属性
        /// </summary>
        public static DependencyProperty ShowCoordinateSystemProperty;

        /// <summary>
        /// 显示坐标系
        /// </summary>
        public bool ShowCoordinateSystem
        {
            get => (bool)base.GetValue(Marker3DCanvas.ShowCoordinateSystemProperty);
            set => base.SetValue(Marker3DCanvas.ShowCoordinateSystemProperty, value);
        }
        #endregion

        #region 显示相机信息 —— bool ShowCameraInfo
        /// <summary>
        /// 显示相机信息依赖属性
        /// </summary>
        public static DependencyProperty ShowCameraInfoProperty;

        /// <summary>
        /// 显示相机信息
        /// </summary>
        public bool ShowCameraInfo
        {
            get => (bool)base.GetValue(Marker3DCanvas.ShowCameraInfoProperty);
            set => base.SetValue(Marker3DCanvas.ShowCameraInfoProperty, value);
        }
        #endregion 

        #region 模型向上方向 —— ThreeDUpDirection ModelUpDirection
        /// <summary>
        /// 模型向上方向依赖属性
        /// </summary>
        public static DependencyProperty ModelUpDirectionProperty;

        /// <summary>
        /// 模型向上方向
        /// </summary>
        public ThreeDUpDirection ModelUpDirection
        {
            get => (ThreeDUpDirection)base.GetValue(Marker3DCanvas.ModelUpDirectionProperty);
            set => base.SetValue(Marker3DCanvas.ModelUpDirectionProperty, value);
        }
        #endregion


        // 3D 相机视角相关

        #region 相机视角 —— ThreeDCamera CameraProperty
        /// <summary>
        /// 相机视角依赖属性
        /// </summary>
        public static DependencyProperty CameraProperty;

        /// <summary>
        /// 相机视角
        /// </summary>
        public ThreeDCamera Camera
        {
            get => (ThreeDCamera)base.GetValue(Marker3DCanvas.CameraProperty);
            set => base.SetValue(Marker3DCanvas.CameraProperty, value);
        }

        #endregion 


        // 3D 模型相关

        #region 3D数模 —— SceneNodeGroupModel3D Model3D
        /// <summary>
        /// 3D数模依赖属性
        /// </summary>
        public static DependencyProperty Model3DProperty;

        /// <summary>
        /// 3D数模
        /// </summary>
        public SceneNodeGroupModel3D Model3D
        {
            get => (SceneNodeGroupModel3D)base.GetValue(Marker3DCanvas.Model3DProperty);
            set => base.SetValue(Marker3DCanvas.Model3DProperty, value);
        }
        #endregion

        #region 数模大小X —— double SizeX
        /// <summary>
        /// 数模大小X依赖属性
        /// </summary>
        public static DependencyProperty SizeXProperty;

        /// <summary>
        /// 数模大小X
        /// </summary>
        public double SizeX
        {
            get => (double)base.GetValue(Marker3DCanvas.SizeXProperty);
            set => base.SetValue(Marker3DCanvas.SizeXProperty, value);
        }
        #endregion

        #region 数模大小Y —— double SizeY
        /// <summary>
        /// 数模大小Y依赖属性
        /// </summary>
        public static DependencyProperty SizeYProperty;

        /// <summary>
        /// 数模大小Y
        /// </summary>
        public double SizeY
        {
            get => (double)base.GetValue(Marker3DCanvas.SizeYProperty);
            set => base.SetValue(Marker3DCanvas.SizeYProperty, value);
        }
        #endregion

        #region 数模大小Z —— double SizeZ
        /// <summary>
        /// 数模大小Z依赖属性
        /// </summary>
        public static DependencyProperty SizeZProperty;

        /// <summary>
        /// 数模大小Z
        /// </summary>
        public double SizeZ
        {
            get => (double)base.GetValue(Marker3DCanvas.SizeZProperty);
            set => base.SetValue(Marker3DCanvas.SizeZProperty, value);
        }
        #endregion

        #region 缩放比例X —— double ScaleX
        /// <summary>
        /// 缩放比例X依赖属性
        /// </summary>
        public static DependencyProperty ScaleXProperty;

        /// <summary>
        /// 缩放比例X
        /// </summary>
        public double ScaleX
        {
            get => (double)base.GetValue(Marker3DCanvas.ScaleXProperty);
            set => base.SetValue(Marker3DCanvas.ScaleXProperty, value);
        }
        #endregion

        #region 缩放比例Y —— double ScaleY
        /// <summary>
        /// 缩放比例Y依赖属性
        /// </summary>
        public static DependencyProperty ScaleYProperty;

        /// <summary>
        /// 缩放比例Y
        /// </summary>
        public double ScaleY
        {
            get => (double)base.GetValue(Marker3DCanvas.ScaleYProperty);
            set => base.SetValue(Marker3DCanvas.ScaleYProperty, value);
        }
        #endregion

        #region 缩放比例Z —— double ScaleZ
        /// <summary>
        /// 缩放比例Z依赖属性
        /// </summary>
        public static DependencyProperty ScaleZProperty;

        /// <summary>
        /// 缩放比例Z
        /// </summary>
        public double ScaleZ
        {
            get => (double)base.GetValue(Marker3DCanvas.ScaleZProperty);
            set => base.SetValue(Marker3DCanvas.ScaleZProperty, value);
        }
        #endregion

        #region X轴补偿 —— double OffsetX
        /// <summary>
        /// X轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetXProperty;

        /// <summary>
        /// X轴补偿
        /// </summary>
        public double OffsetX
        {
            get => (double)base.GetValue(Marker3DCanvas.OffsetXProperty);
            set => base.SetValue(Marker3DCanvas.OffsetXProperty, value);
        }
        #endregion

        #region Y轴补偿 —— double OffsetY
        /// <summary>
        /// Y轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetYProperty;

        /// <summary>
        /// Y轴补偿
        /// </summary>
        public double OffsetY
        {
            get => (double)base.GetValue(Marker3DCanvas.OffsetYProperty);
            set => base.SetValue(Marker3DCanvas.OffsetYProperty, value);
        }
        #endregion

        #region Z轴补偿 —— double OffsetZ
        /// <summary>
        /// Z轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetZProperty;

        /// <summary>
        /// Z轴补偿
        /// </summary>
        public double OffsetZ
        {
            get => (double)base.GetValue(Marker3DCanvas.OffsetZProperty);
            set => base.SetValue(Marker3DCanvas.OffsetZProperty, value);
        }
        #endregion

        #region 旋转轴 —— Vector3D RotateAxis
        /// <summary>
        /// 旋转轴依赖属性
        /// </summary>
        public static DependencyProperty RotateAxisProperty;

        /// <summary>
        /// 旋转轴
        /// </summary>
        public Vector3D RotateAxis
        {
            get => (Vector3D)base.GetValue(Marker3DCanvas.RotateAxisProperty);
            set => base.SetValue(Marker3DCanvas.RotateAxisProperty, value);
        }
        #endregion

        #region 旋转角度 —— double RotateAngle
        /// <summary>
        /// 旋转角度依赖属性
        /// </summary>
        public static DependencyProperty RotateAngleProperty;

        /// <summary>
        /// 旋转角度
        /// </summary>
        public double RotateAngle
        {
            get => (double)base.GetValue(Marker3DCanvas.RotateAngleProperty);
            set => base.SetValue(Marker3DCanvas.RotateAngleProperty, value);
        }
        #endregion


        // 3D 元素相关

        #region 固定大小 —— bool FixedSize
        /// <summary>
        /// 固定大小依赖属性
        /// </summary>
        public static DependencyProperty FixedSizeProperty;

        /// <summary>
        /// 固定大小
        /// </summary>
        public bool FixedSize
        {
            get => (bool)base.GetValue(Marker3DCanvas.FixedSizeProperty);
            set => base.SetValue(Marker3DCanvas.FixedSizeProperty, value);
        }
        #endregion

        #endregion

        #region # 回调方法

        #region 文件路径改变回调方法 —— static async Task FilePathChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 文件路径改变回调方法
        /// </summary>
        private static async void FilePathChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;

            // 重新加载3D数模
            await that.Reload3dModel();

        }
        #endregion

        #region 颜色改变回调方法 —— static async Task ColorChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 颜色改变回调方法
        /// </summary>
        private static async void ColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;

            // 重新加载3D数模材质
            await that.Reload3dModelMaterial();

        }
        #endregion

        #region 使用模式改变回调方法 —— static void UseModelChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 使用模式改变回调方法
        /// </summary>
        private static void UseModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            CanvasUseMode oldValue = (CanvasUseMode)eventArgs.OldValue;
            CanvasUseMode newValue = (CanvasUseMode)eventArgs.NewValue;

            that.ReloadUseMode(newValue);
        }
        #endregion

        #region 操作模式改变回调方法 —— static void OperatorModelChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 操作模式改变回调方法
        /// </summary>
        private static void OperatorModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            CanvasOperatorMode newValue = (CanvasOperatorMode)eventArgs.NewValue;

            // 重置其他按钮状态
            that.ResetToolbarOperatorModeControl();

            that.ReloadOperatorMode();
        }
        #endregion

        #region 模型向上方向改变回调方法 —— static void ModelUpDirectionChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 模型向上方向改变回调方法
        /// </summary>
        private static void ModelUpDirectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            ThreeDUpDirection newValue = (ThreeDUpDirection)eventArgs.NewValue;


            that.ReloadViewportModelUpDirection(newValue);

            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(newValue);
            that.ReloadViewportCamera(defaultCamera);
        }
        #endregion

        #region 相机视角改变回调方法 —— static void CameraChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 相机视角改变回调方法
        /// </summary>
        private static void CameraChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            ThreeDCamera newValue = (ThreeDCamera)eventArgs.NewValue;

            if (newValue == null)
                return;

            that.ReloadViewportCamera(newValue);
            that.ReloadDefaultCamera(newValue);
        }
        #endregion

        #region 缩放比例X改变回调方法 —— static void ScaleXChanged(object sender...
        /// <summary>
        /// 缩放比例X改变回调方法
        /// </summary>
        private static void ScaleXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Y改变回调方法 —— static void ScaleYChanged(object sender...
        /// <summary>
        /// 缩放比例Y改变回调方法
        /// </summary>
        private static void ScaleYChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Z改变回调方法 —— static void ScaleZChanged(object sender...
        /// <summary>
        /// 缩放比例Z改变回调方法
        /// </summary>
        private static void ScaleZChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 画笔改变回调方法 —— static void StrokeThicknessChangedCallback(DependencyObject dependencyObject...
        /// <summary>
        /// 画笔改变回调方法
        /// </summary>
        private static void StrokeThicknessChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;

            // TODO 重新绘制
        }
        #endregion

        #region 绘制按钮列表改变回调方法 —— static void DrawButtonsChangedCallback(DependencyObject dependencyObject...
        /// <summary>
        /// 绘制按钮列表改变回调方法
        /// </summary>
        private static void DrawButtonsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;


            if (that.DrawButtons != null)
                that.DrawButtons.CollectionChanged += that.DrawButtonsItemsChanged;

            that.ReloadDrawButton(that.DrawButtons);
        }
        #endregion

        #region 绘制按钮列表元素改变回调方法 —— void DrawButtonsItemsChanged(object sender...
        /// <summary>
        /// 绘制按钮列表元素改变回调方法
        /// </summary>
        private void DrawButtonsItemsChanged(object sender, NotifyCollectionChangedEventArgs eventArgs)
        {
            ObservableCollection<ThreeDDrawButtonInfo> buttons = (ObservableCollection<ThreeDDrawButtonInfo>)sender;
            this.ReloadDrawButton(buttons);
        }
        #endregion

        #region 标记列表改变回调方法 —— static void MarkersChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 标记列表改变回调方法
        /// </summary>
        private static void MarkersChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Marker3DCanvas that = (Marker3DCanvas)dependencyObject;

            if (that.Markers == null || that._edit)
                return;

            that.Markers.CollectionChanged += that.MarkersItemsChanged;
            that.RedrawMarks(that.Markers);
        }
        #endregion

        #region 标记列表元素改变回调方法 —— void MarkersItemsChanged(object sender...
        /// <summary>
        /// 标记列表元素改变回调方法
        /// </summary>
        private void MarkersItemsChanged(object sender, NotifyCollectionChangedEventArgs eventArgs)
        {
            if (this._edit)
                return;

            ObservableCollection<Marker3DMarker> markers = (ObservableCollection<Marker3DMarker>)sender;
            this.RedrawMarks(markers);
        }
        #endregion

        #endregion

        #region # 路由事件

        #region 3D数模文件加载前路由事件 —— RoutedEvent Load3dModelBefor
        /// <summary>
        /// 3D数模文件加载前路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelBeforEvent;

        /// <summary>
        /// 3D数模文件加载前路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelBefor
        {
            add => base.AddHandler(Marker3DCanvas._Load3dModelBeforEvent, value);
            remove => base.RemoveHandler(Marker3DCanvas._Load3dModelBeforEvent, value);
        }
        #endregion

        #region 3D数模文件加载后路由事件 —— RoutedEvent Load3dModelAfter
        /// <summary>
        /// 3D数模文件加载后路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelAfterEvent;

        /// <summary>
        /// 3D数模文件加载后路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelAfter
        {
            add => base.AddHandler(Marker3DCanvas._Load3dModelAfterEvent, value);
            remove => base.RemoveHandler(Marker3DCanvas._Load3dModelAfterEvent, value);
        }
        #endregion

        #region 标记位置绘制完成路由事件 —— RoutedEvent MarkDrawn
        /// <summary>
        /// 标记位置绘制完成路由事件
        /// </summary>
        private static readonly RoutedEvent _MarkerDrawnEvent;

        /// <summary>
        /// 标记位置绘制完成路由事件处理程序
        /// </summary>
        public event RoutedEventHandler MarkDrawn
        {
            add => base.AddHandler(Marker3DCanvas._MarkerDrawnEvent, value);
            remove => base.RemoveHandler(Marker3DCanvas._MarkerDrawnEvent, value);
        }
        #endregion

        #region 标记位置拖拽完成路由事件 —— RoutedEvent MarkDragCompleted
        /// <summary>
        /// 标记位置拖拽完成路由事件
        /// </summary>
        private static readonly RoutedEvent _MarkDragCompletedEvent;

        /// <summary>
        /// 标记位置拖拽完成路由事件处理程序
        /// </summary>
        public event RoutedEventHandler MarkDragCompleted
        {
            add => base.AddHandler(Marker3DCanvas._MarkDragCompletedEvent, value);
            remove => base.RemoveHandler(Marker3DCanvas._MarkDragCompletedEvent, value);
        }
        #endregion

        #region 标记点击路由事件 —— RoutedEvent MarkClick
        /// <summary>
        /// 标记点击路由事件
        /// </summary>
        private static readonly RoutedEvent _MarkClickEvent;

        /// <summary>
        /// 标记点击路由事件处理程序
        /// </summary>
        public event RoutedEventHandler MarkClick
        {
            add => base.AddHandler(Marker3DCanvas._MarkClickEvent, value);
            remove => base.RemoveHandler(Marker3DCanvas._MarkClickEvent, value);
        }
        #endregion

        #endregion

        #region # 事件处理程序

        #region 打开文件点击事件 —— void OpenFileClick(object sender...
        /// <summary>
        /// 打开文件点击事件
        /// </summary>
        private void OpenFileClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "3D数模文件(*.stl)|*.stl|所有文件|*",
                AddExtension = true,
                RestoreDirectory = true
            };

            if (openFileDialog.ShowDialog() != true)
                return;

            this.FilePath = openFileDialog.FileName;
        }
        #endregion

        #region 重置视角点击事件 —— void ReloadViewingAngleClick(object sender...
        /// <summary>
        /// 重置视角点击事件
        /// </summary>
        private void ReloadViewingAngleClick(object sender, RoutedEventArgs e)
        {
            this.ReloadViewportCamera(this._defaultCamera);
        }
        #endregion

        #region 工具栏操作模式点击事件 —— void ToolbarOperatorModelClick(object sender...
        /// <summary>
        /// 工具栏操作模式点击事件
        /// </summary>
        private void ToolbarOperatorModelClick(object sender, RoutedEventArgs e)
        {
            ToggleButton tbtn = (ToggleButton)sender;

            // 重置其他按钮状态
            this.ResetToolbarOperatorModeControl();

            this._drawType = ThreeDDrawType.Center;

            if (tbtn == this.BtnNormal)
            {
                this.OperationMode = CanvasOperatorMode.Nomal;
            }
            else if (tbtn == this.BtnDrag)
            {
                this.OperationMode = CanvasOperatorMode.Drag;
            }
            else
            {
                this.OperationMode = CanvasOperatorMode.Draw;

                this._checkedDrawButton = this.DrawButtons.SingleOrDefault(i => i.Button == tbtn);

                this._drawType = this._checkedDrawButton!.DrawType;
            }

            // 设置选中
            tbtn.IsChecked = true;
        }
        #endregion

        #region 3D视口鼠标左键按下事件 —— void ViewportMouseLeftDown(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标左键按下事件
        /// </summary>
        public void ViewportMouseLeftDown(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);

            if (!success)
                return;

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Nomal:
                default:
                    break;
                case CanvasOperatorMode.Draw:
                    this.DrawTempMarkPoint(viewport3D, mousePos3D, element3D);
                    break;
                case CanvasOperatorMode.Drag:

                    if (element3D == null)
                        break;

                    if (Keyboard.IsKeyDown(Key.LeftCtrl))
                    {
                        viewport3D.LookAt(element3D.Bounds.Center.ToPoint3D(), 200);
                    }

                    this._selectedElement3D = element3D;
                    break;
            }

        }
        #endregion

        #region 3D视口鼠标右键按下事件 —— void ViewportMouseRightDown(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标右键按下事件
        /// </summary>
        public void ViewportMouseRightDown(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);

            if (!success)
                return;

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Nomal:
                case CanvasOperatorMode.Drag:
                default:
                    break;
                case CanvasOperatorMode.Draw:

                    Mouse.OverrideCursor = Cursors.Arrow;

                    Marker3DMarkerDrawn markerLocation = this.GetMarkerLocation();

                    Marker3DCanvasDrawnEventArgs args = new Marker3DCanvasDrawnEventArgs(Marker3DCanvas._MarkerDrawnEvent, this, markerLocation, this._checkedDrawButton.Tag);
                    this.RaiseEvent(args);

                    break;
            }

        }
        #endregion

        #region 3D视口鼠标移动事件 —— void ViewportMouseMove(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标移动事件
        /// </summary>
        public async void ViewportMouseMove(object sender, MouseEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);

            // 重新加载鼠标指针
            this.ReloadCursor(success, element3D);

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Drag:

                    if (this._selectedElement3D == null || this._selectedElement3D.GetType() != typeof(BillboardTextModel3D))
                        break;

                    this._edit = true;

                    Marker3DMarker selectMarker = (Marker3DMarker)this._selectedElement3D.Tag;

                    if (selectMarker != null)
                    {
                        // 获取鼠标位置
                        Vector2 mousePosition = mousePos2D.ToVector2();

                        // 获取模型位置 
                        Vector3 oldPosition = selectMarker.MarkerLocation.ToVector3();

                        // 获取相机位置
                        Vector3D lookDirction = viewport3D.Camera.LookDirection;
                        Vector3 normalRay3d = lookDirction.ToVector3();

                        // 获取新位置
                        Vector3? newPosition = viewport3D.UnProjectOnPlane(mousePosition, oldPosition, normalRay3d);

                        if (newPosition.HasValue)
                        {
                            selectMarker.MarkerLocation = newPosition.Value.ToPoint3D();

                            this.RedrawMark(selectMarker);
                        }
                    }

                    this._edit = false;

                    break;
                case CanvasOperatorMode.Draw:
                    if (success && element3D == null && Mouse.LeftButton == MouseButtonState.Pressed && this._drawType == ThreeDDrawType.Line)
                        this.DrawTempFeature(mousePos3D.ToVector3());
                    break;
                case CanvasOperatorMode.Nomal:
                default:
                    break;
            }

        }
        #endregion

        #region 3D视口鼠标松开事件 —— void ViewportMouseUp(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标松开事件
        /// </summary>
        public void ViewportMouseUp(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Drag:

                    if (this._selectedElement3D == null || this._selectedElement3D.GetType() != typeof(BillboardTextModel3D))
                        break;

                    Marker3DMarker selectMarker = (Marker3DMarker)this._selectedElement3D.Tag;

                    this.RaiseEvent(new Marker3DCanvasDragCompletedEventArgs(Marker3DCanvas._MarkDragCompletedEvent, this, selectMarker));
                    break;
                case CanvasOperatorMode.Draw:
                case CanvasOperatorMode.Nomal:
                default:
                    break;
            }

            this._edit = false;
            this._selectedElement3D = null;
        }
        #endregion

        #region 3D视口相机改变事件 —— void ViewportCameraChanged(object sender, RoutedEventArgs e)
        /// <summary>
        /// 3D视口相机改变事件
        /// </summary>
        private void ViewportCameraChanged(object sender, RoutedEventArgs e)
        {
            if (this.Viewport3Dx.Camera == null)
                return;

            this.Camera.LookDirection = this.Viewport3Dx.Camera.LookDirection;
            this.Camera.Position = this.Viewport3Dx.Camera.Position;
            this.Camera.UpDirection = this.Viewport3Dx.Camera.UpDirection;

            this.Light3D.Direction = this.Camera.LookDirection;
        }
        #endregion

        #region 3D视口渲染完毕事件 —— void OnViewportRendered(object sender, System.EventArgs e)
        /// <summary>
        /// 3D视口渲染完毕事件
        /// </summary>
        private void OnViewportRendered(object sender, System.EventArgs e)
        {
            if (this._isFirstRender)
                return;

            if (this._sceneRoot == null)
                return;

            this._isFirstRender = true;

            ThreeDLoadFileAfterEventArgs argAfters = new ThreeDLoadFileAfterEventArgs(Marker3DCanvas._Load3dModelAfterEvent, this);
            this.RaiseEvent(argAfters);
        }
        #endregion

        #endregion

        #region # 方法

        // Public 

        #region 截图 —— async Task PrintScreenToFile(string filePath)
        /// <summary>
        /// 截图
        /// </summary>
        public async Task PrintScreenToFile(string filePath)
        {
            await Task.Run(() =>
            {
                string rootPath = Path.GetDirectoryName(filePath);
                if (rootPath == null)
                    throw new Exception("文件路径获取失败！");

                if (!Directory.Exists(rootPath))
                {
                    Directory.CreateDirectory(rootPath);
                }

                this.Viewport3Dx.SaveScreen(filePath);

            });
        }
        #endregion

        #region 截图 —— async Task PrintScreenToBytes()
        /// <summary>
        /// 截图
        /// </summary>
        public async Task<byte[]> PrintScreenToBytes()
        {
            return await Task.Run(() =>
            {
                using MemoryStream memoryStream = this.Viewport3Dx.RenderToBitmapStream();

                return memoryStream.ToArray();
            });
        }
        #endregion


        // Private

        #region 加载3D视口默认相机 —— void LoadDefaultViewportCamera()
        /// <summary>
        /// 加载3D视口默认相机
        /// </summary>
        private void LoadDefaultViewportCamera()
        {
            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(this.ModelUpDirection);

            PerspectiveCamera camera = (PerspectiveCamera)this.Viewport3Dx.Camera;
            camera.LookDirection = defaultCamera.LookDirection;
            camera.UpDirection = defaultCamera.UpDirection;
            camera.Position = defaultCamera.Position;
            camera.NearPlaneDistance = 0.125;
            camera.FarPlaneDistance = double.PositiveInfinity;
            camera.FieldOfView = 30;
        }
        #endregion

        #region 重新加载3D视口模型向上方向 —— void ReloadViewportModelUpDirection()
        /// <summary>
        /// 重新加载3D视口模型向上方向
        /// </summary>
        private void ReloadViewportModelUpDirection(ThreeDUpDirection threeDUpDirection)
        {
            this.Viewport3Dx.ModelUpDirection = threeDUpDirection switch
            {
                ThreeDUpDirection.X => new Vector3D(1, 0, 0),
                ThreeDUpDirection.Y => new Vector3D(0, 1, 0),
                ThreeDUpDirection.Z => new Vector3D(0, 0, 1),
                _ => new Vector3D(0, 1, 0),
            };
        }
        #endregion

        #region 重置工具栏操作模式组件 —— void ResetToolbarOperatorModeControl()
        /// <summary>
        /// 重置工具栏操作模式组件
        /// </summary>
        private void ResetToolbarOperatorModeControl()
        {
            this.BtnNormal.IsChecked = false;
            this.BtnDrag.IsChecked = false;

            foreach (ThreeDDrawButtonInfo button in this.DrawButtons)
            {
                button.Button.IsChecked = false;
            }

            this._tempPointsVisual3D?.Points?.Clear();
        }
        #endregion

        #region 重新加载栏操作模式 —— void ReloadOperatorMode()
        /// <summary>
        /// 重新加载栏操作模式
        /// </summary>
        private void ReloadOperatorMode()
        {
            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Drag:
                    this.BtnDrag.IsChecked = true;
                    break;
                case CanvasOperatorMode.Nomal:
                default:
                    this.BtnNormal.IsChecked = true;
                    break;
            }
        }
        #endregion

        #region 重新加载使用模式 —— void ReloadUseMode(CanvasUseMode newUseModel)
        /// <summary>
        /// 重新加载使用模式
        /// </summary>
        /// <param name="useModel">使用模式</param>
        private void ReloadUseMode(CanvasUseMode useModel)
        {
            this.BtnOpenFile.Visibility = Visibility.Collapsed;
            this.BtnResetViewAngle.Visibility = Visibility.Visible;
            this.BtnNormal.Visibility = Visibility.Visible;
            this.BtnDrag.Visibility = Visibility.Collapsed;

            this.BtnNormal.IsChecked = true;
            this.BtnDrag.IsChecked = false;

            foreach (ThreeDDrawButtonInfo button in this.DrawButtons)
            {
                button.Button.Visibility = Visibility.Collapsed;
                button.Button.IsChecked = false;
            }

            switch (useModel)
            {
                case CanvasUseMode.Nomal:
                default:

                    break;
                case CanvasUseMode.Edit:

                    this.BtnOpenFile.Visibility = Visibility.Visible;
                    this.BtnDrag.Visibility = Visibility.Visible;

                    foreach (ThreeDDrawButtonInfo button in this.DrawButtons)
                    {
                        button.Button.Visibility = Visibility.Visible;
                    }

                    break;
            }
        }
        #endregion

        #region 重新加载相机 —— void ReloadDefaultCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载相机
        /// </summary>
        private void ReloadDefaultCamera(ThreeDCamera camera)
        {
            this._defaultCamera.UpDirection = camera.UpDirection;
            this._defaultCamera.Position = camera.Position;
            this._defaultCamera.LookDirection = camera.LookDirection;
        }
        #endregion

        #region 重新加载3D视口相机 —— void ReloadViewportCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载3D视口相机
        /// </summary>
        private void ReloadViewportCamera(ThreeDCamera camera)
        {
            this.Viewport3Dx.Camera.LookDirection = camera.LookDirection;
            this.Viewport3Dx.Camera.UpDirection = camera.UpDirection;
            this.Viewport3Dx.Camera.Position = camera.Position;
        }
        #endregion

        #region 重新加载3D数模 ——  void Reload3dModel()
        /// <summary>
        /// 重新加载3D数模
        /// </summary>
        private async Task Reload3dModel()
        {
            ThreeDLoadFileBeforEventArgs argBefors = new ThreeDLoadFileBeforEventArgs(Marker3DCanvas._Load3dModelBeforEvent, this);
            this.RaiseEvent(argBefors);

            this._isFirstRender = false;

            string filePath = this.FilePath;

            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                this.Model3D?.Clear();
                return;
            }

            using Importer importer = new Importer();

            HelixToolkitScene scene = await Task.Run(() => importer.Load(filePath));
            this._sceneRoot = scene.Root;

            //加载数模
            this.Model3D = new SceneNodeGroupModel3D();
            this.Model3D.AddNode(this._sceneRoot);

            // 重新加载3D数模补偿信息
            this.Reload3dModelOffset();

            // 重新加载3D数模材质
            await this.Reload3dModelMaterial();
        }
        #endregion

        #region 重新加载3D数模补偿信息 —— void Reload3dModelOffset()
        /// <summary>
        /// 重新加载3D数模补偿信息
        /// </summary>
        private void Reload3dModelOffset()
        {
            if (this._sceneRoot == null)
                return;

            BoundingBox bounds = this.CalculateModelBounds(this._sceneRoot, SharpDX.Matrix.Identity);

            this.OffsetX = 0 - Math.Abs(bounds.Center.X) * this.ScaleX;
            this.OffsetY = 0 - Math.Abs(bounds.Center.Y) * this.ScaleY;
            this.OffsetZ = 0 - Math.Abs(bounds.Center.Z) * this.ScaleZ;

            this.Viewport3Dx.ZoomExtents(0);

            this.Viewport3Dx.FrontView();

            this.ReloadDefaultCamera(this.Camera);

            this.SizeX = bounds.Size.X * this.ScaleX;
            this.SizeY = bounds.Size.Y * this.ScaleY;
            this.SizeZ = bounds.Size.Z * this.ScaleZ;
        }
        #endregion

        #region 计算3D数模界限 —— BoundingBox CalculateModelBounds(SceneNode node...
        /// <summary>
        /// 计算3D数模界限
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="parentTransform">父节点矩阵</param>
        /// <returns>界限</returns>
        private BoundingBox CalculateModelBounds(SceneNode node, SharpDX.Matrix parentTransform)
        {
            BoundingBox bounds = new BoundingBox();
            SharpDX.Matrix nodeTransform = node.ModelMatrix * parentTransform;

            if (node is MeshNode meshNode)
            {
                MeshGeometry3D mesh = meshNode.Geometry as MeshGeometry3D;
                if (mesh?.Positions != null)
                {
                    foreach (Vector3 pos in mesh.Positions)
                    {
                        Vector3 transformedPos = Vector3.TransformCoordinate(pos, nodeTransform);
                        bounds = BoundingBox.Merge(bounds, new BoundingBox(transformedPos, transformedPos));
                    }
                }
            }

            foreach (SceneNode child in node.Items)
            {
                BoundingBox childBounds = this.CalculateModelBounds(child, nodeTransform);
                bounds = BoundingBox.Merge(bounds, childBounds);
            }

            return bounds;
        }
        #endregion

        #region 重新加载3D数模材质 —— void Reload3dModelMaterial()
        /// <summary>
        /// 重新加载3D数模材质
        /// </summary>
        private async Task Reload3dModelMaterial()
        {
            Color color = this.Color;

            IEnumerable<SceneNode> sceneNodes = this.Model3D.GroupNode.Traverse();

            await Task.Run(() =>
            {
                foreach (SceneNode sceneNode in sceneNodes)
                {
                    if (!(sceneNode is MaterialGeometryNode materialGeometryNode))
                        continue;

                    PhongMaterial phongMaterial = new PhongMaterial
                    {
                        DiffuseColor = Color.FromArgb(255, color.R, color.G, color.B).ToColor4(),
                        SpecularColor = Color.FromScRgb(1f, 0.005f, 0.005f, 0.005F).ToColor4()
                    };

                    materialGeometryNode.Material = phongMaterial;

                }
            });

        }
        #endregion

        #region 重新加载鼠标指针 —— void ReloadCursor(bool findNearestSuccess, Element3D element3D)
        /// <summary>
        /// 重新加载鼠标指针
        /// </summary>
        /// <param name="findNearestSuccess">查找边缘是否成功</param>
        /// <param name="element3D">3D元素</param>
        private void ReloadCursor(bool findNearestSuccess, Element3D element3D)
        {
            if (!findNearestSuccess)
            {
                if (Mouse.OverrideCursor != null)
                    Mouse.OverrideCursor = null;
                return;
            }

            if (element3D == null)
            {
                switch (this.OperationMode)
                {
                    case CanvasOperatorMode.Nomal:
                    case CanvasOperatorMode.Drag:
                    default:
                        break;
                    case CanvasOperatorMode.Draw:
                        Mouse.OverrideCursor = Cursors.Cross;
                        break;
                }
            }
            else if (element3D.GetType() == typeof(BillboardTextModel3D))
            {
                switch (this.OperationMode)
                {
                    case CanvasOperatorMode.Nomal:
                    default:
                        Mouse.OverrideCursor = Cursors.Hand;
                        break;
                    case CanvasOperatorMode.Draw:
                        break;
                    case CanvasOperatorMode.Drag:
                        Mouse.OverrideCursor = Cursors.SizeAll;
                        break;
                }
            }

        }
        #endregion

        #region 重新加载绘制按钮 —— void ReloadDrawButton(ObservableCollection<ThreeDDrawButtonInfo> buttons)
        /// <summary>
        /// 重新加载绘制按钮
        /// </summary> 
        /// <param name="buttons">按钮列表</param>
        private void ReloadDrawButton(ObservableCollection<ThreeDDrawButtonInfo> buttons)
        {
            foreach (ToggleButton toggleButton in this._drawButtons)
            {
                this.ToolBarContainer.Children.Remove(toggleButton);
            }
            this._drawButtons.Clear();

            if (buttons == null)
                return;

            foreach (ThreeDDrawButtonInfo buttonInfo in buttons)
            {
                ToggleButton toggleButton = new ToggleButton
                {
                    ToolTip = $"绘制{buttonInfo.Text}",
                    Style = (Style)Application.Current.FindResource("X.ToggleButton.Styles.ToolBar"),
                    Content = new WrapPanel
                    {
                        HorizontalAlignment = HorizontalAlignment.Center,
                        VerticalAlignment = VerticalAlignment.Center,
                        Children =
                        {
                            new SharpVectors.Converters.SvgViewbox
                            {
                                IsHitTestVisible = false,
                                Margin = new Thickness(6),
                                Width = 20,
                                Height = 20,
                                Source = buttonInfo.DrawType == ThreeDDrawType.Line
                                    ? new Uri("pack://application:,,,/X.WPF.Controls;component/Assets/Icons/Line.svg", UriKind.Absolute)
                                    : new Uri("pack://application:,,,/X.WPF.Controls;component/Assets/Icons/Location.svg", UriKind.Absolute)
                            },
                            new TextBlock
                            {
                                Text = buttonInfo.Text,
                                FontSize = 14,
                                Margin = new Thickness(5, 0, 5, 0),
                                FontWeight = FontWeights.Bold,
                                VerticalAlignment = VerticalAlignment.Center
                            }
                        }
                    }
                };
                toggleButton.Click += this.ToolbarOperatorModelClick;

                this.ToolBarContainer.Children.Add(toggleButton);

                buttonInfo.Button = toggleButton;
            }

        }
        #endregion

        #region 清理自定义3D元素 —— void ClearCustomerElement3D()
        /// <summary>
        /// 清理自定义3D元素
        /// </summary>
        private void ClearCustomerElement3D()
        {
            while (true)
            {
                if (this._firstElement3DIndex >= this.Viewport3Dx.Items.Count)
                    return;

                Application.Current.Dispatcher.Invoke(() =>
                {
                    this.Viewport3Dx.Items.RemoveAt(this._firstElement3DIndex);
                });
            }
        }
        #endregion

        #region 绘制临时标记点 —— void DrawTempMarkPoint(Viewport3DX viewport3D, Point3D position...
        /// <summary>
        /// 绘制临时标记点
        /// </summary>
        /// <param name="viewport3D">3D视口</param>
        /// <param name="position">位置</param>
        /// <param name="element3D">3D节点</param>
        private void DrawTempMarkPoint(Viewport3DX viewport3D, Point3D position, Element3D element3D)
        {
            if (element3D != null)
                return;

            if (this._tempPointsVisual3D == null)
            {
                double size = this.StrokeThickness;
                this._tempPointsVisual3D = new PointsVisual3D();
                this._tempPointsVisual3D.Figure = PointFigure.Ellipse;
                this._tempPointsVisual3D.Color = Colors.Red;
                this._tempPointsVisual3D.Size = new Size(size, size);
                this._tempPointsVisual3D.FixedSize = false;

                this.Viewport3Dx.Items.Add(this._tempPointsVisual3D);
            }

            Point3D location = new Point3D(position.X, position.Y, position.Z);

            this._tempPointsVisual3D.Points.Add(location);
        }
        #endregion

        #region 获取标记位置 —— Marker3DMarkerDrawn GetMarkerLocation()
        /// <summary>
        /// 创建标记点
        /// </summary>
        private Marker3DMarkerDrawn GetMarkerLocation()
        {
            if (this._tempPointsVisual3D.Points.Count < 2 && this._drawType != ThreeDDrawType.Point)
                return null;

            Point3D originalPoint;

            switch (this._drawType)
            {
                case ThreeDDrawType.Line:
                    int centerIndex = this._tempPointsVisual3D.Points.Count / 2;
                    originalPoint = this._tempPointsVisual3D.Points[centerIndex];
                    break;
                case ThreeDDrawType.Point:
                    originalPoint = this._tempPointsVisual3D.Points.Last();
                    break;
                case ThreeDDrawType.Center:
                default:

                    // 获取中心点
                    double x = this._tempPointsVisual3D.Points.Sum(i => i.X) / this._tempPointsVisual3D.Points.Count;
                    double y = this._tempPointsVisual3D.Points.Sum(i => i.Y) / this._tempPointsVisual3D.Points.Count;
                    double z = this._tempPointsVisual3D.Points.Sum(i => i.Z) / this._tempPointsVisual3D.Points.Count;
                    originalPoint = new Point3D(x, y, z);

                    break;
            }

            // 获取中心点向量
            double extendedRate = 2;
            Point3D model3dOriginPoint = new Point3D(0, 0, 0);
            Vector3D centerVector = (originalPoint - model3dOriginPoint) * extendedRate;

            // 获取标记点
            Point3D markerPoint = model3dOriginPoint + centerVector;
            markerPoint.Z = this.Camera.Position.Z >= 0 ? Math.Abs(markerPoint.Z) : 0 - Math.Abs(markerPoint.Z);
            Marker3DMarkerDrawn result = new Marker3DMarkerDrawn
            {
                OriginLocation = originalPoint,
                MarkerLocation = markerPoint,
                FeaturePoints = this._drawType == ThreeDDrawType.Line ? this._tempPointsVisual3D?.Points.Select(i => i.ToVector3()).ToList() : new List<Vector3>()
            };

            // 清理临时
            this._tempPointsVisual3D?.Points.Clear();
            this._tempPointsVisual3D = null;

            this.Viewport3Dx.Items.Remove(this._tempFeatureVisual3D);
            this._tempFeatureVisual3D = null;

            return result;
        }
        #endregion

        #region 重新绘制标记列表 —— void RedrawMarks(ObservableCollection<Marker3DMarker> markers)
        /// <summary>
        /// 重新绘制标记列表
        /// </summary>
        private void RedrawMarks(ObservableCollection<Marker3DMarker> markers)
        {
            this.ClearCustomerElement3D();

            foreach (Marker3DMarker marker in markers)
            {
                this.DrawMark(marker);
            }
        }
        #endregion

        #region 重新绘制标记 —— void RedrawMark(Marker3DMarker marker)
        /// <summary>
        /// 重新绘制标记
        /// </summary>
        /// <param name="marker">标记信息</param>
        private void RedrawMark(Marker3DMarker marker)
        {
            this.Viewport3Dx.Items.Remove(marker.LineVisual3D);
            this.Viewport3Dx.Items.Remove(marker.Label.Visual3D);
            this.Viewport3Dx.Items.Remove(marker.Table.Visual3D);

            this.DrawMark(marker);
        }
        #endregion

        #region 绘制标记 —— void DrawMark(Marker3DMarker marker)
        /// <summary>
        /// 绘制标记
        /// </summary>
        /// <param name="marker">标记</param>
        private void DrawMark(Marker3DMarker marker)
        {
            // 绘制标签连接线
            this.DrawMarkerLine(marker);

            if (marker.IsShowLabel)
            {
                // 绘制标签
                this.DrawLable(marker.Name, marker.MarkerLocation, marker);
            }
            else
            {
                // 绘制表格
                this.DrawTable(marker);
            }

            // 绘制特征
            this.DrawFeature(marker);
        }
        #endregion

        #region 绘制标签 —— void DrawLable(string title, Point3D position...
        /// <summary>
        /// 绘制标签
        /// </summary>
        /// <param name="title">标题</param>
        /// <param name="location">位置</param>
        /// <param name="marker">元数据</param>
        private void DrawLable(string title, Point3D location, Marker3DMarker marker)
        {
            // 生成文本信息
            TextInfo textInfo = new TextInfo(title, location.ToVector3())
            {
                Scale = 1
            };

            // 设置文本样式
            BillboardSingleText3D geometry = new BillboardSingleText3D()
            {
                TextInfo = textInfo,
                FontColor = Color.FromArgb(marker.PassColor.A, marker.PassColor.R, marker.PassColor.G, marker.PassColor.B).ToColor4(),
                FontSize = 14,
                BackgroundColor = Color.FromArgb(marker.PassBackgroundColor.A, marker.PassBackgroundColor.R, marker.PassBackgroundColor.G, marker.PassBackgroundColor.B).ToColor4(),
                FontStyle = FontStyles.Normal,
                Padding = new Thickness(5),
                HorizontalAlignment = BillboardHorizontalAlignment.Center,
                VerticalAlignment = BillboardVerticalAlignment.Bottom,
            };

            // 设置3D显示元素
            marker.Label.Visual3D = new BillboardTextModel3D
            {
                Geometry = geometry,
                FixedSize = this.FixedSize,
                Tag = marker,
            };

            this.Viewport3Dx.Items.Add(marker.Label.Visual3D);
        }
        #endregion

        #region 绘制连接线 —— LinesVisual3D DrawLableLine(Point3D startPosition...
        /// <summary>
        /// 绘制标签连接线
        /// </summary>
        /// <param name="marker">标记信息</param> 
        private void DrawMarkerLine(Marker3DMarker marker)
        {
            LinesVisual3D linesVisual3D = new LinesVisual3D();
            linesVisual3D.Points.Add(marker.OriginLocation);
            linesVisual3D.Points.Add(marker.MarkerLocation);
            linesVisual3D.IsClosed = false;
            linesVisual3D.Color = Colors.Red;
            linesVisual3D.Thickness = 0.5;

            this.Viewport3Dx.Items.Add(linesVisual3D);

            marker.LineVisual3D = linesVisual3D;
        }
        #endregion

        #region 绘制表格 —— void DrawTable(Marker3DMarker marker)
        /// <summary>
        /// 绘制表格
        /// </summary>
        /// <param name="marker">标记信息</param> 
        private void DrawTable(Marker3DMarker marker)
        {
            // 绘制图像
            using SKBitmap skBitmap = this.DrawTableImage(marker);
            using MemoryStream imageStream = skBitmap.ToMemoryStream();

            // 加载图片
            BillboardImage3D geometry = new BillboardImage3D(TextureModel.Create(imageStream));
            geometry.ImageInfos.Add(new ImageInfo
            {
                Position = marker.MarkerLocation.ToVector3(),
                UV_TopLeft = new Vector2(0, 0),
                UV_BottomRight = new Vector2(1, 1),
                Width = skBitmap.Width,
                Height = skBitmap.Height,
                HorizontalAlignment = BillboardHorizontalAlignment.Center,
                VerticalAlignment = BillboardVerticalAlignment.Bottom

            });

            // 加载内容3D元素
            marker.Table.Visual3D = new BillboardTextModel3D
            {
                Geometry = geometry,
                FixedSize = this.FixedSize,
                Tag = marker,

            };
            marker.Table.Visual3D.MouseDown3D += (sender, args) =>
            {
                Marker3DCanvasMarkerClickEventArgs eventArgs = new Marker3DCanvasMarkerClickEventArgs(Marker3DCanvas._MarkClickEvent, this, marker);
                this.RaiseEvent(eventArgs);
                Mouse.OverrideCursor = Cursors.Arrow;
            };
            this.Viewport3Dx.Items.Add(marker.Table.Visual3D);
        }
        #endregion

        #region 绘制表格图片 —— SKBitmap DrawTableImage(List<Marker3DCanvasMarkerContentCm> markerContents)
        /// <summary>
        /// 绘制表格图片
        /// </summary>
        /// <param name="marker">标记信息</param> 
        private SKBitmap DrawTableImage(Marker3DMarker marker)
        {
            Marker3DTableCell lastColumnHeaderCell = marker.Table.ColumnHeaderRow.Cells.LastOrDefault();

            // 绘制表格 
            SKBitmap tableImage = this.DrawTableImage(marker.Table);

            // 创建画布
            int mainImageWidth = tableImage.Width + marker.Table.Visual3DSpace * 2;
            int mainImageHeight = tableImage.Height + marker.Table.Visual3DSpace * 2 + lastColumnHeaderCell.Height + marker.Table.Visual3DSpace;
            SKBitmap mainSkBitmap = new SKBitmap(mainImageWidth, mainImageHeight);
            using SKCanvas maxinCanvas = new SKCanvas(mainSkBitmap);
            maxinCanvas.DrawColor(SKColor.Parse("#E9E9E9"));

            // 绘制标题 
            float titleX = marker.Table.Visual3DSpace + 1;
            float titleY = marker.Table.Visual3DSpace + lastColumnHeaderCell.Height / 2f;
            this.DrawText(maxinCanvas, System.Drawing.Color.Black, titleX, titleY, marker.Name, false, true);

            // 绘制检测结果
            Marker3DTableCell passCell = new Marker3DTableCell(marker.Pass)
            {
                X = marker.Table.Visual3DSpace + lastColumnHeaderCell.X,
                Y = marker.Table.Visual3DSpace,
                Width = lastColumnHeaderCell.Width,
                Height = lastColumnHeaderCell.Height,
                Color = marker.PassColor,
                BackgroundColor = marker.PassBackgroundColor,
            };
            SKBitmap passCellImage = this.DrawCellImage(passCell);
            maxinCanvas.DrawBitmap(passCellImage, passCell.X, marker.Table.Visual3DSpace);

            // 绘制表格
            float tableX = marker.Table.Visual3DSpace;
            float tableY = marker.Table.Visual3DSpace + passCellImage.Height + marker.Table.Visual3DSpace / 2f;
            maxinCanvas.DrawBitmap(tableImage, tableX, tableY);

            return mainSkBitmap;
        }
        #endregion

        #region 绘制表格图片 —— SKBitmap DrawTableImage(Marker3DTable table)
        /// <summary>
        /// 绘制表格图片
        /// </summary>
        private SKBitmap DrawTableImage(Marker3DTable table)
        {
            const int space = 1;
            int columnSpaceTotal = (table.ColumnHeaderRow.Cells.Count + 1) * space;
            int columnWidthTotal = table.ColumnHeaderRow.Cells.Sum(i => i.Width);
            const int rowHeight = 25;
            int rowSpaceTotal = (1 + table.Rows.Count + 1) * space;
            int rowHeightTotal = (1 + table.Rows.Count) * rowHeight;
            int width = columnSpaceTotal + columnWidthTotal;
            int height = rowSpaceTotal + rowHeightTotal;

            SKBitmap tableSkBitmap = new SKBitmap(width, height);
            using SKCanvas tableSkCanvas = new SKCanvas(tableSkBitmap);
            tableSkCanvas.DrawColor(SKColor.Parse("#BFBFBF"));

            // 绘制列头行
            this.DrawRowImage(tableSkCanvas, rowHeight, space, 0, space, table.ColumnHeaderRow.Cells);

            // 绘制内容行
            float offsetY = rowHeight + space;
            foreach (Marker3DTableRow row in table.Rows)
            {
                float rowY = space + offsetY;
                offsetY = rowHeight + rowY;
                this.DrawRowImage(tableSkCanvas, rowHeight, space, 0, rowY, row.Cells);
            }

            return tableSkBitmap;
        }
        #endregion

        #region 绘制行图片 —— void DrawRowImage(SKCanvas canvas, int height...
        /// <summary>
        /// 绘制行
        /// </summary> 
        private void DrawRowImage(SKCanvas canvas, int height, int space, float x, float y, List<Marker3DTableCell> cells)
        {
            float offsetX = x;
            foreach (Marker3DTableCell cell in cells)
            {
                cell.X = space + offsetX;
                cell.Y = y;
                cell.Height = height;
                using SKBitmap cellSkBitmap = this.DrawCellImage(cell);
                canvas.DrawBitmap(cellSkBitmap, cell.X, cell.Y);

                offsetX = cell.Width + cell.X;
            }
        }
        #endregion

        #region 绘制单元格图片 —— SKBitmap DrawCellImage(Marker3DTableCell cell...
        /// <summary>
        /// 绘制单元格图片
        /// </summary>
        private SKBitmap DrawCellImage(Marker3DTableCell cell, bool isXCenter = true, bool isYCenter = true)
        {
            SKBitmap cellSkBitmap = new SKBitmap(cell.Width, cell.Height);
            using SKCanvas cellSkCanvas = new SKCanvas(cellSkBitmap);
            cellSkCanvas.DrawColor(new SKColor(cell.BackgroundColor.R, cell.BackgroundColor.G, cell.BackgroundColor.B, cell.BackgroundColor.A));

            cell.ContentX = cell.Width / 2f;
            cell.ContentY = cell.Height / 2f;
            this.DrawText(cellSkCanvas, cell.Color, cell.ContentX, cell.ContentY, cell.Content, isXCenter, isYCenter);

            return cellSkBitmap;
        }
        #endregion

        #region 绘制文字 —— SKRect DrawText(SKCanvas canvas, System.Drawing.Color color...
        /// <summary>
        /// 绘制文字
        /// </summary>
        private SKRect DrawText(SKCanvas canvas, System.Drawing.Color color, float x, float y, string content, bool isXCenter = false, bool isYCenter = false)
        {
            // 创建画笔
            SKPaint paint = new SKPaint
            {
                Color = new SKColor(color.R, color.G, color.B),
                IsAntialias = true,
                TextSize = 14f,
                Typeface = SKTypeface.FromFamilyName("微软雅黑", SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright),
                TextAlign = SKTextAlign.Left
            };

            // 测量内容大小
            SKRect textBounds = SKRect.Empty;
            paint.MeasureText(content, ref textBounds);
            SKRect chineseBounds = SKRect.Empty;
            paint.MeasureText("中文", ref chineseBounds);

            // 绘制内容
            float locationX = isXCenter ? x - textBounds.Width / 2 : x;
            float locationY = chineseBounds.Height + y;
            locationY = isYCenter ? locationY - chineseBounds.Height / 2 : locationY;
            canvas.DrawText(content, locationX, locationY, paint);

            return textBounds;
        }
        #endregion

        #region 绘制临时特征 —— void DrawTempFeature(Vector3 position)
        /// <summary>
        /// 绘制临时特征
        /// </summary>
        /// <param name="position">位置</param>
        private void DrawTempFeature(Vector3 position)
        {
            this._tempPointsVisual3D ??= new PointsVisual3D();

            this._tempPointsVisual3D.Points.Add(position.ToPoint3D());
            List<Vector3> points = this._tempPointsVisual3D.Points.Select(i => i.ToVector3()).ToList();


            this.Viewport3Dx.Items.Remove(this._tempFeatureVisual3D);
            this._tempFeatureVisual3D = new MeshGeometryModel3D
            {
                Geometry = this.GetFeatureMeshGeometry(points),
                Material = PhongMaterials.Red
            };

            this.Viewport3Dx.Items.Add(this._tempFeatureVisual3D);
        }
        #endregion

        #region 绘制特征 —— void DrawFeature(Marker3DMarker marker)
        /// <summary>
        /// 绘制特征
        /// </summary>
        /// <param name="marker">标记</param>
        private void DrawFeature(Marker3DMarker marker)
        {

            if (marker.Feature.Points.Count == 0)
                return;

            PhongMaterial phongMaterial = new PhongMaterial
            {
                DiffuseColor = Color.FromArgb(255, marker.PassColor.R, marker.PassColor.G, marker.PassColor.B).ToColor4(),
                SpecularColor = Color.FromScRgb(1f, 0.005f, 0.005f, 0.005F).ToColor4()
            };

            marker.Feature.Visual3D = new MeshGeometryModel3D
            {
                Geometry = this.GetFeatureMeshGeometry(marker.Feature.Points),
                Material = this.UseMode == CanvasUseMode.Nomal ? phongMaterial : PhongMaterials.Red
            };
            this.Viewport3Dx.Items.Add(marker.Feature.Visual3D);
        }
        #endregion

        #region 获取特征3D材质 —— Geometry3D GetFeatureMeshGeometry(Vector3 position...
        /// <summary>
        /// 获取缺陷3D材质
        /// </summary>
        /// <param name="points">点位集</param>
        /// <returns>3D材质</returns>
        private Geometry3D GetFeatureMeshGeometry(List<Vector3> points)
        {
            MeshBuilder meshBuilder = new MeshBuilder();
            double diameter = this.StrokeThickness;
            int thetaDiv = 12;
            double radio = diameter / 2d;

            meshBuilder.AddTube(points, diameter, thetaDiv, false);

            if (points.Count >= 2)
            {
                meshBuilder.AddEllipsoid(points.First(), radio, radio, radio);
                meshBuilder.AddEllipsoid(points.Last(), radio, radio, radio);
            }

            return meshBuilder.ToMeshGeometry3D();
        }
        #endregion

        #endregion

    }
}